#pragma once

#include <stdint.h>
#include <array>

namespace Graph
{

/** @class Matrix
  * @brief Класс матрицы векторного преобразования.
  *
  * Вещественная матрица 4 х 4. Хранится в памяти по строкам.
  */
class Matrix
{
public:
    const static int32_t ROWS_COUNT = 4;
    const static int32_t COLS_COUNT = 4;
    const static int32_t ELEMENTS_COUNT = ROWS_COUNT * COLS_COUNT;

public:
    /**
      * Конструктор по умолчанию.
      * Создает заполненую нулями матрицу.
      */
    Matrix();

    /**
      * Создает матрицу, все элементы которой равны одному числу.
      *
      * @param value значение, присваемое всем элементам матрицы.
      */
    Matrix(const double value);

    /**
      * Конструктор копирования.
      *
      * @param other копируемая матрица.
      */
    Matrix(const Matrix &other);

    /**
      * Конструктор матрицы из массива эелементов.
      *
      * @param data массив из 16ти занчений элементов матрицы.
      *             Первые 4 элемента соответсвуют первой строке
      *             следующие 4 элемента второй строке и т.д.
      */
    Matrix(const std::array<double, ELEMENTS_COUNT>& data);

    /**
      * Деструктор.
      */
    virtual ~Matrix();

    /**
      * Возвращает указатель на первый элемент i-ой строки
      * в массиве элементов.
      *
      * @param номер строк. Строки нумеруются с нуля.
      * @return const указатель на начало i-ой строки.
      */
    const double * operator [](const int32_t i) const;

    /**
      * Возвращает указатель на первый элемент i-ой строки
      * в массиве элементов.
      *
      * @param номер строк. Строки нумеруются с нуля.
      * @return указатель на начало i-ой строки.
      */
    double * operator [](const int32_t i);

    /**
      * Поэлементно сравнивает матрицы.
      *
      * @param other матрица для сравнения.
      * @return true если матрицы равны, иначе false.
      */
    bool operator ==(const Matrix &other) const;

    /**
      * Поэлементно сравнивает матрицы.
      *
      * @param other матрица для сравнения.
      * @return true если матрицы не равны, иначе false.
      */
    bool operator !=(const Matrix &other) const;

    /**
      * Поэлементно копирует матрицы.
      *
      * @param other матрица для копирования.
      * @return *this.
      */
    Matrix& operator =(const Matrix &other);

    /**
      * Поэлементно складывает матрицы.
      *
      * @param other матрица для сложения.
      * @return *this.
      */
    Matrix& operator +=(const Matrix &other);

    /**
      * Поэлементно вычитает матрицы.
      *
      * @param other вычитаемая матрица.
      * @return *this.
      */
    Matrix& operator -=(const Matrix &other);

    /**
      * Поэлементно умножает матрицу на число.
      *
      * @param a сомножитель.
      * @return *this.
      */
    Matrix& operator *=(const double a);

    /**
      * Поэлементно делит матрицу на число.
      *
      * @param a делитель.
      * @return *this.
      */
    Matrix& operator /=(const double a);

    /**
      * Меняет знак элементов матрицы.
      *
      * @return -1 * (*this).
      */
    Matrix operator -() const;

    /**
      * Возврашает указатель на начало массива элементов.
      *
      * @return const указатель на начло массива элементов.
      */
    const double * data() const;

    /**
      * Возврашает указатель на начало массива элементов.
      *
      * @return указатель на начло массива элементов.
      */
    double * data();

protected:
    std::array<double, ELEMENTS_COUNT> m_data; ///< массив элементов.
};

/**
  * Поэлементно складывает матрицы.
  *
  * @param m1 первое слагаемое.
  * @param m2 второе слагаемое.
  * @return m1 + m2.
  */
Matrix operator +(const Matrix &m1, const Matrix& m2);

/**
  * Поэлементно вычитает матрицы.
  *
  * @param m1 первое слагаемое.
  * @param m2 второе слагаемое.
  * @return m1 - m2.
  */
Matrix operator -(const Matrix &m1, const Matrix& m2);

/**
  * Оператор матричного умножения.
  *
  * @param m1 первый сомножитель.
  * @param m2 второй сомножитель.
  * @return m1 * m2.
  */
Matrix operator *(const Matrix &m1, const Matrix& m2);

/**
  * Поэлементно умножает матрицу на число.
  *
  * @param m матрица.
  * @param a множитель.
  * @return a * m.
  */
Matrix operator *(const Matrix &m, const double a);

/**
  * Поэлементно умножает матрицу на число.
  * @see operator *(const Matrix &m, const double a).
  *
  * @param a множитель.
  * @param m матрица.
  * @return a * m.
  */
Matrix operator *(const double a, const Matrix &m);

/**
  * Поэлементно делит матрицу на число.
  *
  * @param m матрица.
  * @param a делитель.
  * @return a * m.
  */
Matrix operator /(const Matrix &m, const double a);

}
